package com.fj.redisson.aspect;

import com.fj.redisson.annotation.ConcurrencySafe;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Aspect@Order(value = Integer.MAX_VALUE-1)
public class ConcurrencySafeConfiguration {

    @Autowired
    private RedissonClient redisson;

    @Pointcut("@annotation(com.fj.redisson.annotation.ConcurrencySafe)")
    public void pointcut(){

    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        StringBuilder pk = new StringBuilder();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        pk.append(joinPoint.getTarget().getClass().getSimpleName()).append("_");
        pk.append(method.getName()).append("_");
        ConcurrencySafe annotation = method.getAnnotation(ConcurrencySafe.class);
        Class<?> objClass = annotation.objClass();
        String[] fields = annotation.fields();
        long waitTime = annotation.waitTime();
        for (Object argObj:joinPoint.getArgs()){
            if (argObj.getClass().equals(objClass)){
                for (String field:fields){
                    try {
                        Field declaredField = objClass.getDeclaredField(field);
                        declaredField.setAccessible(true);
                        String fieldValue = (String) declaredField.get(argObj);
                        pk.append(fieldValue).append("_");
                    } catch (NoSuchFieldException | IllegalAccessException e) {
                        throw e;
                    }
                }
                break;
            }
        }
        String lockName = pk.append("lock").toString();
        RLock lock = redisson.getLock(lockName);
        boolean b = lock.tryLock(waitTime, TimeUnit.MILLISECONDS);
        if (b){
            try{
                System.out.println(lockName +"_"+Thread.currentThread().getName()+"获取锁...");
                return joinPoint.proceed();
            }catch (Throwable throwable){
                throw throwable;
            }finally {
                lock.unlock();
            }

        }
        throw new RuntimeException("加锁失败");
    }
}
